package com.zyht.common.util;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.zyht.common.bean.PropertiesUtilBean.PropertiesUtils;

public class FileUtil {
	static final Logger logger = LoggerFactory.getLogger(FileUtil.class);
	private static final String FOLDER_SEPARATOR = "/";
	private static final char EXTENSION_SEPARATOR = '.';
	static Map<String, PropertiesHold> propertiesCache = new ConcurrentHashMap<String, PropertiesHold>();
	public static SimpleDateFormat sf = new SimpleDateFormat("yyyy"+File.separator+"MM"+File.separator+"dd");
	
	/**
	 * 本地文件地址
	 */
	public static String file_upload_sys_dir = PropertiesUtils.getConfig("file_upload_sys_dir");
	
	/**
	 * 随机生成本地文件路径
	 * @param fileName
	 * @return String[0] 相对配置文件路径地址  String[1]绝对路径
	 */
	public static String[] randomLocalFilePath(String fileName){
		String uploadPath = FileUtil.file_upload_sys_dir;
		String nowStr = sf.format(new Date());
		String ext = getFilenameExtension(fileName);
		String appendPath = File.separator + nowStr + File.separator + System.currentTimeMillis()+ new Random().nextInt(6) + "." +ext;
		String realPath = uploadPath + appendPath;
		return new String[]{appendPath,realPath};
	}
	
	public static String getFilenameExtension(String path) {
		if (path == null) {
			return null;
		}
		int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
		if (extIndex == -1) {
			return null;
		}
		int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
		if (folderIndex > extIndex) {
			return null;
		}
		return path.substring(extIndex + 1);
	}
	
	/**
	 * 上传
	 * @param fileStream 待写文件
	 * @param targetPath 绝对路径
	 * @return String[0]-文件绝对路径     null-写文件失败
	 */
	public static String[] writeFile(InputStream fileStream, String targetPath){
		try {
			String realPath = targetPath;
			if(FileUtil.checkBak(realPath)){
			}
			File filePath = new File(realPath);	
			try {
				logger.debug("文件["+filePath.getAbsolutePath()+"]开始写入");
				FileUtils.copyInputStreamToFile(fileStream, filePath);
			} catch (IOException e) {
				logger.error("文件["+filePath.getAbsolutePath()+"]写入异常："+e.getMessage());
				throw e;
			}
			return new String[]{realPath};
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static List<List<String>> readCsv(String filePath) {
		List<List<String>> list = new ArrayList<List<String>>();
		try {
			FileInputStream fis = new FileInputStream(filePath);
			DataInputStream in = new DataInputStream(fis);
			BufferedReader br = new BufferedReader(new InputStreamReader(in,
					"GBK"));// , "UTF-8"
			// 读取直到最后一行
			String line = "";
			while ((line = br.readLine()) != null) {
				// 跳过空行
				if ("".equals(line)) {
					continue;
				}
				// 把一行数据分割成多个字段
				StringTokenizer st = new StringTokenizer(line, ",");
				List<String> lineList = new ArrayList<String>();
				while (st.hasMoreTokens()) {
					lineList.add(st.nextToken());
				}
				list.add(lineList);
			}
			br.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	public static String writeLocalFile(InputStream fileStream, String fileName) throws Exception{
		String[] path =randomLocalFilePath(fileName);
		String[] path2 =writeFile(fileStream, path[1]);
		if(path2!=null){
			return path2[0];
		}else{
			throw new Exception("写文件失败");
		}
	}
	
	public static String writeLocalFile(MultipartFile hongbaofile) throws Exception{
		return writeLocalFile(hongbaofile.getInputStream(), hongbaofile.getOriginalFilename());
	}
	
	public static String getWebAppPath() {
		return getWebAppDirectory().getPath()+File.separator;
	}
	
	public static File getWebAppDirectory() {
		File file = getWebAppDirectory2();
		if (file == null)
			file = getWebAppDirectory1();

		return file;
	}

	private static File getWebAppDirectory1() {
		String currenFilePath = "";
		File file = null;
		try {
			currenFilePath = FileUtil.class.getResource("").getPath();

			file = new File(currenFilePath);
			currenFilePath = Thread.currentThread().getContextClassLoader()
					.getResource("").getPath();
			file = new File(currenFilePath);
			while (!(file.getName().toUpperCase().equals("WEB-INF")))
				file = file.getParentFile();

			File webAppDirFile = file.getParentFile();
			return webAppDirFile;
		} catch (Exception e) {
			logger.error("get WebAppDirectory exception", e);
		}

		return null;
	}

	private static File getWebAppDirectory2() {
		String currenFilePath = "";
		File file = null;
		try {
			currenFilePath = FileUtil.class.getResource("").getPath();

			file = new File(currenFilePath);
			while (!(file.getName().toUpperCase().equals("WEB-INF")))
				file = file.getParentFile();

			File webAppDirFile = file.getParentFile();
			return webAppDirFile;
		} catch (Exception e) {
			logger.error("get class path exception", e);
		}
		return null;
	}

	public static File getProjectDataDirectory() {
		File webAppDirectory = getWebAppDirectory();
		logger.debug(webAppDirectory.getPath());
		File projectDirectoryFile = webAppDirectory.getParentFile();
		logger.debug(projectDirectoryFile.getPath());
		return new File(projectDirectoryFile, "data");
	}

	public static File getFileInDataDirectory(String fileName) {
		return new File(getProjectDataDirectory(), fileName);
	}

	public static String getBaseUploadfilePath() {
		return getWebAppDirectory().getPath() + File.separator + "upload"
				+ File.separator + "UserFiles" + File.separator + "File"
				+ File.separator;
	}

	public static Properties loadProperties(String path, long expirateSeconds) {
		PropertiesHold ph = (PropertiesHold) propertiesCache.get(path);
		if ((ph == null)
				|| (expirateSeconds * 1000L < System.currentTimeMillis()
						- ph.birth)) {
			logger.info("{} reloaded with the expiration:{}s", path,
					Long.valueOf(expirateSeconds));
			ClassPathResource rs = new ClassPathResource(path);
			Properties p = new Properties();
			try {
				p.load(rs.getInputStream());
			} catch (IOException e) {
				logger.warn(path + " load failed", e);
				throw new IllegalStateException(e.getMessage());
			}
			ph = new PropertiesHold(System.currentTimeMillis(), p);
			propertiesCache.put(path, ph);
		}
		return ph.properties;
	}

	public static String getAbsFileName(String fileName) {
		int indx = fileName.lastIndexOf("/");
		String absName = null;
		if (indx != -1)
			absName = fileName.substring(indx);
		return absName;
	}

	public static String getClassPath() {
		String path = getClassPath2();
		if (StringUtils.isEmpty(path))
			path = getClassPath1();

		return path;
	}

	private static String getClassPath1() {
		String currenFilePath = "";
		File file = null;
		try {
			currenFilePath = FileUtil.class.getResource("").getPath();

			file = new File(currenFilePath);
			while (!(file.getName().toUpperCase().equals("CLASSES")))
				file = file.getParentFile();

			return file.getAbsolutePath();
		} catch (Exception e) {
			logger.error("获取classpath 异常", e);
		}
		return null;
	}

	private static String getClassPath2() {
		String currenFilePath = "";
		File file = null;
		try {
			currenFilePath = Thread.currentThread().getContextClassLoader()
					.getResource("").getPath();
			file = new File(currenFilePath);
			while (!(file.getName().toUpperCase().equals("CLASSES")))
				file = file.getParentFile();

			return file.getAbsolutePath();
		} catch (Exception e) {
			logger.error("get class path exception", e);
		}

		return null;
	}

	static class PropertiesHold {
		long birth;
		Properties properties;

		public PropertiesHold(long birth, Properties properties) {
			this.birth = birth;
			this.properties = properties;
		}
	}

	/**
	 * 
	 * @param path
	 * @return
	 */
	public static List<String> getDirFileName(String path) {
		File file = new File(path);
		List<String> list = new ArrayList<String>();
		if (file.isDirectory()) {
			File[] dirFile = file.listFiles();
			for (File f : dirFile) {
				list.add(f.getAbsolutePath());
			}
		}
		return list;
	}

	public static void main(String[] args) {
		System.out.println(JSON
				.toJSON(getDirFileName("./WebRoot/WEB-INF/classes/fonts")));
		;
	}

	/**
	 * 以字节为单位读取文件，常用于读二进制文件，如图片、声音、影像等文件。
	 */
	public static String readFileByBytes(String fileName) {
		InputStream in = null;

		try {
			System.out.println("以字节为单位读取文件内容，一次读多个字节：");
			// 一次读多个字节
			byte[] tempbytes = new byte[1024];
			//int byteread = 0;
			in = new FileInputStream(fileName);
			// showAvailableBytes(in);
			// 读入多个字节到字节数组中，byteread为一次读入的字节数
			StringBuffer buffer = new StringBuffer();
			while (( in.read(tempbytes)) != -1) {
				// System.out.write(tempbytes, 0, byteread);
				buffer.append(tempbytes);
			}
			return buffer.toString();
		} catch (Exception e1) {
			e1.printStackTrace();
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e1) {
				}
			}
		}
		return "";
	}

	/**
	 * 以字符为单位读取文件，常用于读文本，数字等类型的文件
	 */
	public static void readFileByChars(String fileName) {
		File file = new File(fileName);
		Reader reader = null;
		try {
			System.out.println("以字符为单位读取文件内容，一次读一个字节：");
			// 一次读一个字符
			reader = new InputStreamReader(new FileInputStream(file));
			int tempchar;
			while ((tempchar = reader.read()) != -1) {
				// 对于windows下，\r\n这两个字符在一起时，表示一个换行。
				// 但如果这两个字符分开显示时，会换两次行。
				// 因此，屏蔽掉\r，或者屏蔽\n。否则，将会多出很多空行。
				if (((char) tempchar) != '\r') {
					System.out.print((char) tempchar);
				}
			}
			reader.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			System.out.println("以字符为单位读取文件内容，一次读多个字节：");
			// 一次读多个字符
			char[] tempchars = new char[30];
			int charread = 0;
			reader = new InputStreamReader(new FileInputStream(fileName));
			// 读入多个字符到字符数组中，charread为一次读取字符数
			while ((charread = reader.read(tempchars)) != -1) {
				// 同样屏蔽掉\r不显示
				if ((charread == tempchars.length)
						&& (tempchars[tempchars.length - 1] != '\r')) {
					System.out.print(tempchars);
				} else {
					for (int i = 0; i < charread; i++) {
						if (tempchars[i] == '\r') {
							continue;
						} else {
							System.out.print(tempchars[i]);
						}
					}
				}
			}

		} catch (Exception e1) {
			e1.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e1) {
				}
			}
		}
	}

	/**
	 * 以行为单位读取文件，常用于读面向行的格式化文件
	 */
	public static void readFileByLines(String fileName) {
		File file = new File(fileName);
		BufferedReader reader = null;
		try {
			System.out.println("以行为单位读取文件内容，一次读一整行：");
			reader = new BufferedReader(new FileReader(file));
			String tempString = null;
			int line = 1;
			// 一次读入一行，直到读入null为文件结束
			while ((tempString = reader.readLine()) != null) {
				// 显示行号
				System.out.println("line " + line + ": " + tempString);
				line++;
			}
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e1) {
				}
			}
		}
	}

	/**
	 * 随机读取文件内容
	 */
	public static void readFileByRandomAccess(String fileName) {
		RandomAccessFile randomFile = null;
		try {
			System.out.println("随机读取一段文件内容：");
			// 打开一个随机访问文件流，按只读方式
			randomFile = new RandomAccessFile(fileName, "r");
			// 文件长度，字节数
			long fileLength = randomFile.length();
			// 读文件的起始位置
			int beginIndex = (fileLength > 4) ? 4 : 0;
			// 将读文件的开始位置移到beginIndex位置。
			randomFile.seek(beginIndex);
			byte[] bytes = new byte[10];
			int byteread = 0;
			// 一次读10个字节，如果文件内容不足10个字节，则读剩下的字节。
			// 将一次读取的字节数赋给byteread
			while ((byteread = randomFile.read(bytes)) != -1) {
				System.out.write(bytes, 0, byteread);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (randomFile != null) {
				try {
					randomFile.close();
				} catch (IOException e1) {
				}
			}
		}
	}

	/**
	 * 显示输入流中还剩的字节数
	 */
//	private static void showAvailableBytes(InputStream in) {
//		try {
//			System.out.println("当前字节输入流中的字节数为:" + in.available());
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}

	/**
	 * 根据输入的文件与输出流对文件进行打包
	 * 
	 * @param File
	 * @param org
	 *            .apache.tools.zip.ZipOutputStream
	 */
	public static void zipFile(File inputFile, ZipOutputStream ouputStream) {
		try {
			if (inputFile.exists()) {
				/**
				 * 如果是目录的话这里是不采取操作的， 至于目录的打包正在研究中
				 */
				if (inputFile.isFile()) {
					FileInputStream IN = new FileInputStream(inputFile);
					BufferedInputStream bins = new BufferedInputStream(IN, 512);
					// org.apache.tools.zip.ZipEntry
					ZipEntry entry = new ZipEntry(inputFile.getName());
					ouputStream.putNextEntry(entry);
					// 向压缩文件中输出数据
					int nNumber;
					byte[] buffer = new byte[512];
					while ((nNumber = bins.read(buffer)) != -1) {
						ouputStream.write(buffer, 0, nNumber);
					}
					// 关闭创建的流对象
					bins.close();
					IN.close();
				} else {
					try {
						File[] files = inputFile.listFiles();
						for (int i = 0; i < files.length; i++) {
							zipFile(files[i], ouputStream);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 把接受的全部文件打成压缩包
	 * 
	 * @param List
	 *            <File>;
	 * @param org
	 *            .apache.tools.zip.ZipOutputStream
	 */
	public static void zipFile(List<File> files, ZipOutputStream outputStream) {
		int size = files.size();
		for (int i = 0; i < size; i++) {
			File file = (File) files.get(i);
			zipFile(file, outputStream);
		}
	}

	/**
	 * 删除文件
	 * 
	 * @param path
	 * @return
	 */
	public static boolean forceDelete(String path) {
		try {
			File file = new File(path);
			if (file.exists()) {
				FileUtils.forceDelete(file);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	 * 检查文件是否已创建, 若以创建则重命名备份, 留出
	 * 
	 * @param fileName
	 * @return
	 */
	public static boolean checkBak(String fileName){
		File file= new File(fileName);
		if(file.exists()){
			for (int i = 0; i < Integer.MAX_VALUE; i++) {
				File file2 = new File(fileName+".("+i+").bak");
				if(file2.exists()){
					continue;
				}
				return file.renameTo(file2);
			}
			return false;
		}else{
			return true;
		}
	}
	
	/**
	 * 判断文件是否是图片，如果是，返回true，否则，返回false
	 * 
	 * @param file
	 *            需要验证的File文件
	 * @return true or false
	 */
	public static boolean fileIsImage(File file) {
		InputStream is = null;
		BufferedReader reader = null;
		boolean res = true;
		try {
			// 将文件转换成输入流
			is = new FileInputStream(file);
			// 用image IO读取文件，如果文件file不是图片，则为null
			BufferedImage image = ImageIO.read(is);
			if (image != null) { // 如果image不为空，则说明file文件是图片
				reader = new BufferedReader(new FileReader(file));
				String exits = null;
				while ((exits = reader.readLine()) != null) {
					exits = shiftD(exits);
					if (exits.indexOf("eval(") > 0
							|| exits.indexOf("<?php") > 0) {
						res = false;
					}
				}
			}
		} catch (Exception e) {
			res = false;
			e.printStackTrace();
		} finally {
			try {
				if (is != null) {
					is.close();
				}
				if (reader != null) {
					reader.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return res;
	}

	public static String shiftD(String str) {
		int size = str.length();
		char[] chs = str.toCharArray();
		for (int i = 0; i < size; i++) {
			if (chs[i] <= 'Z' && chs[i] >= 'A') {
				chs[i] = (char) (chs[i] + 32);
			}
		}
		return new String(chs);
	}
}